查看原文
其他

深入分布式缓存之EVCache探秘开局篇(文末赠书)

深入分布式缓存 中生代技术 2022-11-14

深入分布式缓存

读完需要

15分钟

速读仅需 5 分钟

云服务不仅为软件系统的开发和部署带来了更多的敏捷性,而且提供了更多创新的可能性。当分布式缓存技术遇到云服务会是怎样的情形呢?EVCache 就是这样的一种技术。

EVCache 是一个开源、快速的分布式缓存,是基于 Memcached 的内存存储和 Spymem-cached 客户端实现的解决方案,主要用在亚马逊弹性计算云服务(AWS EC2)的基础设施上,为云计算做了优化,能够顺畅而高效地提供数据层服务。图 10-1 所示是 EVCache 开源项目在 Github 上的表现。

图 10-1 EVCache 开源项目的 Star 趋势



EVCache 是一个缩写,包括:

  • Ephemeral:数据存储是短暂的,有自身的存活时间。

  • Volatile:数据可以在任何时候消失。

  • Cache:一个内存型的键值对存储系统。

EVCache 实现的主要功能包括分布式键值对存储,亚马逊云服务的跨区域数据复制以及注册和自动发现新节点或者新服务。EVCache 典型的应用是对上下文一致性要求不高的场景,其可扩展性已经可以处理非常大的流量,同时提供了健壮的应用编程接口。

1


   

EVCache 项目介绍

EVCache 是 Netflix 开源软件项目(Open Source Software,OSS)中的一部分,是 Netflix 多个关于数据存储的开源项目中的一个重要成员。在 Netflix 架构中有两个基本元素,一个是控制平面,运行在亚马逊云服务(AWS)之上,用于用户登录,浏览和播放以及一般性服务。另一个是数据平面,叫做 Open Connect,这是一个全球性的视频分发网络。

EVCache 是位于控制平面的。

Netflix 是微服务架构领域的实践者,在系统中部署了上百个微服务,每一个微服务只专注做一件事情。这使得 Netflix 所提供的软件系统能够做到高度均衡和松耦合。由于状态都存储在缓存或持久存储中,所以这些微服务大多数是无状态的,易于自动扩展。

EVCache 在 Netflix 内部是一个被广泛使用的数据缓存服务,所提供的低延迟且高可用的缓存方案可以很好地满足 Netflix 微服务架构需要,也用来做一般数据的存储。EVCache 能够使面向终端用户的应用,个性化算法和各种微服务都具备优良的性能。

EVCache 具有如下的特性:

  • 分布式的键值对存储,缓存可以跨越多个实例。

  • 数据可以跨越亚马逊云服务的可用区进行复制。

  • 通过 Netflix 内部的命名服务进行注册,自动发现新节点和服务。

  • 为了存储数据,键是非空字符串,值可以是非空的字节数组,基本类型,或者序列化对象,且小于 1 MB。

  • 作为通用的缓存集群被各种应用使用,支持可选的缓存名称,通过命名空间避免主键冲突。

  • 一般的缓存命中率在 99%以上。

  • 与 Netfix 驻留数据框架能够良好协作,典型的访问次序:内存→ EVCache → Cassandre/SimpleDB/S3。

使用缓存技术所带来的最大影响可能是数据的不一致性。出于性能优先的考虑,具体的应用会依赖于 EVCache 来处理数据的不一致性。对于存活时间很短的数据,用 TTL 设置数据的失效时间,对于长时间保留的数据,通过构建一致性检查来修复它们。

EVCache 是使用了 Memeached 操作接口(如 get、set、touch 等),基于数据大小和网络容量可以线性扩展,支持任意数量的数据备份(有的集群支持 2 个,有的支持 9 个)。所有操作都拥有对拓扑结构的感知、重试、回退,以及其他机制来保障操作的完整性,同时优化了亚马逊云服务的架构。每个主键中的数据通过数据分块技术处理后可以是任意大小的。

简而言之,Memcached 是一个单进程应用,在单台主机上工作的很好,而 EVCache 使用它作为一个基础模块,Memcached 是 EVCache 的一个子集。

1.1


   

EVCache 的由来

对于一个流媒体服务来说,提供一个以客户为中心的用户体验意味着要做很多事情,要包括优秀的内容库,直观的用户界面,个性化内容推荐,可以让用户获取所喜爱的内容并可高质量播放的快速服务,等等。

Netflix 期待用户和系统服务交互时能有一个极致的用户体验,对云服务而言,所考虑的目标是:

  • 与 Netflix 数据中心相对应的快速响应时间。

  • 从面向会话的应用到云服务中的无会话状态应用。

  • 使用 NoSQL 的数据驻留,如 Cassandra/SimpleDB/S3。

从数据存储(如 Cassandra,或其他的亚马逊云服务如 S3 或 SimpleDB)中计算或提取数据,这样的数据存储操作大多需要花费数百毫秒,因此会影响用户体验。通过 EVCache 作为数据前端缓存,访问时间更加快速而且是线性的,同时削减了这些数据存储的负载,还能够更有效的分担用户请求。此外,数据加载服务经常是先于缓存响应,这保证了用户可以得到个性化的数据响应而不是通用响应。另外,使用 EVCache 缓存可以有效地削减操作的总体成本。

EVCache 是典型的客户端/服务器结构。服务器端包括一个 Memcached 进程,这是一个流行的且久经考验的内存型键值对存储,还包括一个叫 Prana 的 Java 进程用于与发现服务(基于 Eureka 的实现)通信并托管本地管理,以及监控服务健康状态和统计状态的各种应用,并将统计信息发送给 Netfix 平台的统计服务。具体结构如图 10-2 所示。

图 10-2 EVCache Server 的基本结构

其中,面向微服务的 Java 应用提供了一个集成应用程序到微服务生态系统的 HTTP 接口,主要功能如下:

  • 注册到发现系统。

  • 其他服务的发现。

  • 健康检查服务。

  • HTTP API 和负载均衡要求。

  • 动态属性加载。

EVCache 客户端是一个 Java 的客户端,用于发现 EVCache 服务器并管理所有的增删改查(CRUD)操作,由客户端处理在集群中添加/删除服务器。基于亚马逊云服务可用区,客户端在执行创建、更新和删除操作的时候复制数据。另一方面,客户端的读操作直接从同一可用区的服务器读取数据。图 10-3 展示了 EVCache 的典型部署结构和单节点客户端实例与服务器的关系。

图 10-3 EVCache 单节点客户端实例与服务器的关系

一个 EVCache 客户端连接了多个 EVCache 的服务器集群。在一个区域内,Netflix 有多个全数据集的拷贝,由亚马逊云服务的可用区隔离开来。虚线框描述了区域内的副本,每个都拥有数据的全量镜像,作为隔离亚马逊云服务的自动伸缩组来管理这些镜像。某些缓存在一个区域内有两个镜像,有的拥有更多。这种高层架构长期来看是有效的,不会改变。每个客户端连接自己区域内所有可用区的所有服务器。写操作被发往所有实例,读操优先选择离读请求近的服务器。

1.2


   

EVCache 的发展

Netflix 的服务在全球 130 个多个国家上线了,很多国家都可以使用。为了应对用户和服务日益增长的需求,Netflix 在全球建立了 EVCache 分布式系统。

Netflix 的全球云服务遍布亚马逊各个服务区域,例如北弗吉尼亚、俄勒冈州和爱尔兰,为这些地区的会员提供就近服务,但是网络流量会因为各种原因改变,比如关键基础设施出了问题故障,或者地区之间进行失败恢复等,因此,Netflix 采用无态应用服务器服务于来自任何地区的会员。

这些数据如果从持久层存储获得将会非常昂贵(造成频繁的数据库访问),Netflix 需要将这种数据写入到本地缓存,而且必须复制到所有地区的缓存中,以便服务于各个地区的用户请求。微服务是依赖于缓存的,必须快速可靠地访问多种类型的数据,比如会员的观影历史,排行榜和个性化推荐等,这些数据的更新与改变都必须复制到全世界各个地区,以便这些地区的用户能够快速可靠地访问。

EVCache 是专门为这些情况而设计的缓存产品,这是建立在于全局复制基础上的,同时也考虑了强一致性。例如,如果爱尔兰和弗吉尼亚的推荐内容有轻微差别,这些差别不会伤害到用户浏览和观看体验,对于非重要的数据,会严重依赖最终一致性模型进行复制,

本地和全局两个缓存的差别保持在一个可以容忍的很短的时间内,这就大大简化了数据的复制。EVCache 并不需要处理全局锁、事务更新、部分提交回滚或其他分布式一致性有关的复杂问题。即使在跨区域复制变慢的情况下,也不会影响性能和本地缓存的可靠性,所有复制都是异步的,复制系统能够在不影响本地缓存操作的情况下悄悄地进行。复制延迟是另外一个问题,快得足够吗?在两个地区之间切换的会员流量有多频繁?什么情况会冲击导致不一致性?宁愿不从完美主义去设计一个复制系统,EVcache 只要能最低限度满足应用和会员用户的要求即可。

图 10-4 介绍了 EVCache 跨地域的复制

图 10-4 EVCache 跨地域的数据复制

这张图说明复制操作是在 SET 操作以后实现,应用程序调用 EVCache 客户端库的 set 方法,之后的复制路径对于调用者是透明的:

1)EVCache 客户端库发送 SET 到缓存系统的本地地区的一个实例服务器中。

2)EVCache 客户端库同时也将写人元数据(包括 key,但是不包括要缓存的数据本身)到复制消息队列(Kafka)。

3)本地区的复制中继服务将会从这个消息队列中读取消息。

4)中继服务会从本地缓存中抓取符合 key 的数据。

5)中继服务会发送一个 SET 请求到另一个地域的复制中继服务。

6)在另一个区域中,复制中继服务会接受请求,然后执行 SET 操作到它的本地缓存,

完成复制。

7)在接受地区的本地应用当通过 GET 操作以后会在本地缓存上看到这个已经更新的数据值。

这是一个简单描述,需要注意的是,它只会对 SET 操作有效,对于其他 DELETE TOUCH 或批 mutation 等操作不会复制,DELETE 和 TOUCH 是非常类的,只有一点不同:它们不从本地缓存中读取已经存在的值。

跨区域复制主要是通过消息队列进行,一个地区的 EVCache 客户端不会注意到其他地区的复制情况,读写都是只使用本区域缓存,不会和其他地区缓存耦合,通过消息系统来解耦合。

1.3


   

EVCache 的演进

EVCache 作为 Nethix 系统中最大的子系统之一,在系统优化中占有相当的比例,地位独一无二。所有数据存储在内存的成本随着用户基数的增长而上扬,单日个性化批处理输出将加载超过 5 TB 的数据到 EVCache 集群。数据存储成本是存储数据与全局副本个数的乘积。如前所述,不同的 A/B 测试和其他内部数据也增加了更多的数据。对于用户的工作集,如今已经有数十亿的键值,而且在持续增加,成本的压力逐渐显现了出来。

面向数据和时延的优化

在一般情况下,在 Netflix 的某个服务区域内可以看到同一个用户的反复区域切换对用户而言并不是常态。尽管数据在三个区域的内存中,只有一个区域中的数据被所在的用户正常使用。由此推断,在每个区域有着这些缓存的不同工作集,一个小的子集是热数据,其他是冷数据。

除了冷热数据的分类之外,所有在内存中的这些数据存储成本随着用户的基数在增加。

Netflix 使用 EVCache 在内存中存储了若干 TB 的数据,包括了用于弹性的多个数据拷贝。

随着成本面临的压力。Netflix 开始使用 RocksDB 来降低 EVCache 的存储成本,同时保持了相对低的请求延迟。Netflix 引人了多级缓存机制,即同时使用 RAM 和 SSD。

根据不同区域不同数据访问的情况,Netflix 构建了一个系统将热数据存储在 RAM,冷数据存储在硬盘。这是典型的两级缓存架构(L1 代表 RAM,L2 代表硬盘),依赖于 EVCache 的强一致性和低时延性能。面对尽量低的时延需求,要使用更多的昂贵内存,使用低成本的 SSD 也要满足客户端对低时延的预期。

内存型 EVCache 集群运行在 AWS r3 系列的实例类型上,对大规模内存的使用进行了优化。通过转移到 i2 系列的实例上,在相同的 RAM 和 CPU 的条件下,可以获得比 SSD 存储(r3 系)扩大十倍的增益(80 → 800GB,从 r3.xlarge 到 i2.xlarge)。Netflix 也降级了实例的大小到小型内存实例上。结合这两点,就可以在数千台服务器上做优先的成本优化了。

基于 EVCache,这种充分利用全局化请求发布和成本优化的项目叫做 Moneta,源自拉丁记忆女神的名字,也是罗马神话中财富守护神——Juno Moneta。

Moneta 架构

Moneta 项目在 EVCahce 服务器中引入了 2 个新的进程:Rend 和 Mnemonic。Rend 是用 Go 语言写的一个高性能代理,Mnemonic 是一个基于 RocksDB 的硬盘型键值对存储。

Mnemnonic 重用了 Rend 服务器组件来处理协议解析(如 Memcached 协议),连接管理和并行锁。这三种服务器都使用 Memeached 的文本和二进制协议,所以客户端与它们的交互有着相同的语法,给调试和一致性检查带来了便捷性。Moneta 的系统结构如图 10-5 所示。

图 10-5 Moneta 的结构组成

Rend 代理服务

Rend 作为另外两个真正存储数据进程的代理,是一个高性能服务器,使用二进制和文本 Memcached 协议进行通信。它是 Go 语言写的,具有对并发处理的高性能。这个项目已经在 Github 上开源了。使用 Go 是不错的选择,因为需要比 Java 更好的低时延(垃圾回收时的暂停是个问题),以及比 C 更好的生产效率,同时能处理成千上万的客户端连接,Go 非常适合这样的场景。

Rend 的职责是管理 L1 和 L2 缓存的关系,根据不同的内部使用场景采用不同的策略,还具有裁剪数据的特性,能够将数据分割成固定的大小插入到 Memcached 中以避免内存分配时的病态行为。这种服务器侧的分片代替了客户端分片,已经证明是可行的。

Rend 的设计是模块化的,并且可配置。在内部,有这样一些分层:连接管理,服务器循环,通信协议,请求编排和后台处理器。Rend 也有着独立用来测试的客户端代码库,能够集中发现协议中的 bug 或者其他错误,例如错误对齐,未清除的缓存以及未完成的响应等。Rend 的基本结构如图 10-6 所示。

图 10-6 Rend 的结构组成

作为 Moneta 服务的那些缓存,一个服务器就可以服务多种不同的客户端。一类是热路径上的在线分析流量,用户请求的个性化数据。其他是离线分析的流量和近期系统所产生的数据。这些典型的服务是整夜运行的巨量批处理和结束时几个小时的持续写操作。

模块化允许使用默认的实现来优化 Netflix 夜间的批处理计算,直接在 L2 中插入数据并且在 L1 中更换热数据,避免了在夜间预计算时引起 L1 缓存的写风暴。来自其他区域的副本数据通常不是热数据,所以也直接插人 L2。

图 10-7 展示了一个 Rend 进程有多个端口连接了各种后台存储。

图 10-7 Rend多端口连接后台存储服务

鉴于 Rend 的模块化,很容易在不同的端口上引入其他的服务器,几行代码就能实现批处理和流量副本。允许不同后台的插件式嵌人,通过一个接口和一个构造函数即可。已经证明了这种设计的有效性,一个工程师在一天内熟悉了相关代码并学习了 LMDB,把它集成起来作为了存储后台。这些代码参见 https://github.com/Netflix/rend-Imdb ( https://github.com/Netflix/rend-Imdb )。

Mnemonic 存储

Mnemonic 是基于 RocksDB 的 L2 解决方案,在硬盘上存储数据。协议解析、连接管理、Mnemonic 的并发控制等所有的管理都使用了和 Rend 相同的库。Mnemonic 是嵌入到 Moneta 服务器的一个后台服务,Mnemonic 项目暴露出一个定制化的 C API 供 Rend 处理器使用。Mnemonic 的基本结构如图 10-8 所示。

图 10-8 Mnemonic 的基本结构组成

Mnemonic 中有趣的部分是在 C++的核心层封装了 RocksDB。Mnemonic 处理 Memcached 协议风格的请求,实现了 Memeached 的所需操作,包括 TTL 支持。它包含了一个重要的特性:将请求分发到一个本地系统的多个 RocksDB 数据库,减少了每个 RocksDB 数据库实例的负载。

在研究过有效访问 SSD 的几种技术之后,Netflix 选择了 RocksDB,一个嵌入式键值对存储,它使用了日志结构合并树的数据结构。写操作首先插入到一个内存数据结构中(一个内存表),当写满的时候再写入到硬盘上。当写入硬盘的时候,内存表是一个不可修改的 SST 文件。这样形成了批量的序列化写人 SSD 的操作,减少了大量的内部垃圾回收,改善了 SSD 在长时间运行实例上的时延。

Netflix 开始使用有层次的精简配置,主要原因是在多个数据库中分发请求。然而,当评估生产数据的精简配置以及与生产环境类似的流量的时候,发现这样的配置会引起大量额外的 SSD 读写,增加了时延。SSD 读流量经常达到 200MB/sec。评估时的流量包括了长时间的高写操作,仿真了每天的批处理计算进程。在此期间,RocksDB 持续的移动 L0 记录达到了一个很高的水平,放大成为非常高的写操作。

为了避免过载,Netflix 切换到 FIFO 型的精简配置。在这种配置中,没有真正的精简操作被完成。基于数据库的最大尺寸,删除旧的 SST 文件。记录在硬盘的 level 0 中,所以只在多个 SST 文件中按时间排序。这种配置的下降趋势在于读操作必须在判断一个键是否命中之前以时间倒序检查每个 SST 文件。这种检查通常不需要硬盘读操作,RocksDB 中的大量过滤器杜绝了高比例的对每个 SST 的硬盘访问。然而,有利有弊,SST 文件的数量影响了赋值操作的有效性,将低于正常有层次风格的精简配置。Netflix 通过初始进入系统时的读写请求在多个 RocksDB 进行分发减少了扫描多个文件的负面影响。(未完待续)

- EOF -


文末留言,分享你对缓存的理解与看法,截止到2021年1月15日12点,精选后点赞数前5名,获得作者签名书《深入分布式缓存》一本




想要加入中生代架构群的小伙伴,请添加群合伙人大白的微信

申请备注(姓名+公司+技术方向)才能通过哦!

阿里技术精彩文章推荐

往期推荐



深度:揭秘阿里巴巴的客群画像

多隆:从工程师到阿里巴巴合伙人

阿里技术专家楚衡:架构制图的工具与方法论

蚂蚁集团技术专家山丘:性能优化常见压测模型及优缺点

阿里文娱技术专家战獒: 领域驱动设计详解之What, Why, How?

阿里专家马飞翔:一文读懂架构整洁之道

阿里专家常昊:新人如何上手项目管理?

蚂蚁集团沈凋墨:Kubernetes-微内核的分布式操作系统

阿里合伙人范禹:常挂在阿里技术人嘴边的四句土话

阿里技术专家都铎:一文搞懂技术债

支付宝研究员兼OceanBase总架构师杨传辉:我在数据库梦之队的十年成长路

阿里技术专家麒烨:修炼测试基本功

阿里计算平台掌门人贾扬清:我对人工智能方向的一点浅见

蚂蚁资深算法专家周俊:从原理到落地,支付宝如何打造保护隐私的共享智能?

阿里高级技术专家箫逸:如何画好一张架构图?

阿里高级技术专家张建飞:应用架构分离业务逻辑和技术细节之道

蚂蚁科技 Service Mesh 落地实践与挑战 | GIAC 实录

阿里6年,我的技术蜕变之路!

蚂蚁集团涵畅:再启程,Service Mesh 前路虽长,尤可期许

阿里P9专家右军:大话软件质量稳定性

阿里合伙人程立:阿里15年,我撕掉了身上两个标签

阿里高工流生 | 云原生时代的 DevOps 之道

阿里高级技术专家邱小侠:微服务架构的理论基础 - 康威定律

阿里P9专家右军:以终为始的架构设计

阿里P8架构师:淘宝技术架构从1.0到4.0的架构变迁!12页PPT详解

阿里技术:如何画出一张合格的技术架构图?

蚂蚁资深技术专家王旭:开源项目是如何让这个世界更安全的?

阿里资深技术专家崮德:8 个影响我职业生涯的重要技能

阿里儒枭:我看技术人的成长路径

阿里高级技术专家宋意:平凡人在阿里十年的成长之旅

阿里技术专家甘盘:浅谈双十一背后的支付宝LDC架构和其CAP分析

阿里技术专家光锥:亿级长连网关的云原生演进之路

阿里云原生张羽辰:服务发现技术选型那点事儿

蚂蚁研究员玉伯:做一个简单自由有爱的技术人

阿里高级技术专家至简: Service Mesh 在超大规模场景下的落地挑战

阿里巴巴山猎:手把手教你玩转全链路监控

阿里涉江:你真的会学习吗?从结构化思维说起

蚂蚁金服资深技术专家经国:云原生时代微服务的高可用架构设计


   END     

#架构师必备#

点分享点点赞点在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存